// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using Aspire.Hosting.ConsoleLogs;
using OpenTelemetry.Proto.Collector.Logs.V1;
using System.Collections.Generic;

namespace iPanel.Otlp;

public sealed class OtlpConsoleLogService
{
    private readonly ILogger<OtlpConsoleLogService> _logger;

    // 内存泄漏{ 如果服务器确定下线后， 删除对应的 nodeid  }
    static readonly Dictionary<string, KeyValuePair<Channel<string>, LogEntries>> keyValuePairs = new();
    static readonly List<Action<string, Channel<string>, LogEntries>> subscriptions = [];

    public OtlpConsoleLogService(ILogger<OtlpConsoleLogService> logger)
    {
        _logger = logger;
    }

    public void Export(ExportRequest request)
    {
        if (keyValuePairs.TryGetValue(request.NodeId, out var kv) == false)
        {
            lock (keyValuePairs)
            {
                if (keyValuePairs.TryGetValue(request.NodeId, out kv) == false)
                {
                    kv = new KeyValuePair<Channel<string>, LogEntries>(Channel.CreateBounded<string>(new BoundedChannelOptions(100)
                    {
                        FullMode = BoundedChannelFullMode.DropOldest
                    }), new(100));
                    keyValuePairs.TryAdd(request.NodeId, kv);
                    foreach (var item in subscriptions) item.Invoke(request.NodeId, kv.Key, kv.Value);
                }
            }
        }
        if (kv.Key is not null)
        {
            kv.Key.Writer.TryWrite(request.Message);
        }
    }

    public Dictionary<string, KeyValuePair<Channel<string>, LogEntries>> GetNodes() => keyValuePairs;

    public void RemoveByNode(string nodeid)
    {
        lock (keyValuePairs)
        {
            if (keyValuePairs.ContainsKey(nodeid)) keyValuePairs.Remove(nodeid);
        }
    }

    public void RemoveNoExists(IEnumerable<string> nodeids)
    {
        lock (keyValuePairs)
        {
            var noExists = keyValuePairs
                .Where(_ => nodeids.Contains(_.Key) == false)
                .Select(_ => _.Key)
                .ToList();
            foreach (var item in noExists) keyValuePairs.Remove(item);
        }
    }

    public void OnNewNodes(Action<string, Channel<string>, LogEntries> callback, CancellationToken cancellation)
    {
        if (cancellation.IsCancellationRequested) return;
        subscriptions.Add(callback);
        cancellation.Register(() =>
        {
            subscriptions.Remove(callback);
        });
    }
}
